#include "frame.h"
#include <sstream>

#define num2asc(x) ((x)+0x30)
#define asc2num(x) ((x)-0x30)
namespace can{

void frame::preCheck(int dlc) const
{
    if(dlc<0)
        dlc=mDLC.to_ulong();
    if(dlc>8) throw std::out_of_range("max data length is 64 bytes");
}
void frame::postCheck(std::string& rawFrame) const
{
    //length check
    int len=rawFrame.length(), expectedLen=0,dlc=0;
    if(len>=39)
    {
        if(rawFrame[13]==num2asc(kDominantBit))   //IDE=0
        {
            dlc=std::stoul(rawFrame.substr(15,4),nullptr,2);       //(std::bitset<4> (rawFrame.substr(15,4))).to_ulong();
            if(dlc>8)   dlc=8;
            if (rawFrame[12]==num2asc(kDominantBit))  //RTR=0
            {
                expectedLen=1+12+6+25;  //44
                expectedLen+=dlc*8;
            }else   //RTR=1
                expectedLen=1+12+6+25;
        }else   //IDE=1
        {
            dlc=std::stoul(rawFrame.substr(35,4),nullptr,2);        //(std::bitset<4> (rawFrame.substr(35,4))).to_ulong();
            if(dlc>8)   dlc=8;
            if(rawFrame[32]==num2asc(kDominantBit))   //RTR=0
            {
                expectedLen=1+32+6+25;
                expectedLen+=dlc*8;
            }else   //RTR=1
                expectedLen=1+32+6+25;
        }
        if(len!=expectedLen)
            throw std::length_error("The length of data/remote frame is invalid.");
    }else
        throw std::length_error("The length of data/remote frame is invalid.");
    //bit validation check
    if(rawFrame[0]!=this->mSOFrame.to_string().at(0))
        throw std::invalid_argument("the SOF bit of frame does not match");
    if(rawFrame[len-10]!=this->mCRC_del.to_string().at(0))
        throw std::invalid_argument("the delimiter right after CRC does not match");
    if(rawFrame[len-8]!=this->mACK_del.to_string().at(0))
        throw std::invalid_argument("the delimiter right after ACK does not match");
    if(rawFrame.substr(len-7,7)!=this->mEOFrame.to_string())
        throw std::invalid_argument("the EOF field of frame does not match");
}
std::string frame::getWholeFrameInOne(bool user) const
{
    std::stringstream wf;   //stands for Whole Frame
    bool gd=this->fmt.test(fmt_e::groupDisplay);
    bool hex=this->fmt.test(fmt_e::hex);
    bool crc=this->fmt.test(fmt_e::crc);
    if(user && hex) {wf.unsetf(std::ios::dec);wf.setf(std::ios::hex);}

    if(!user || (user&&!gd))
        wf<<this->mSOFrame<<this->mIdentifier;
    else if(hex)
        wf<<"| "<<this->mSOFrame<<" | "<<this->mIdentifier.to_ulong()<<" | ";
    else
        wf<<"| "<<this->mSOFrame<<" | "<<this->mIdentifier<<" | ";

    if(this->mIDE.test(0))
    {
        if(!user || (user&&!gd))
            wf<<this->mSRR<<this->mIDE<<this->mIdentifierExt<<this->mRTR
            <<this->mR1<<this->mR0<<this->mDLC;
        else if(hex)
            wf<<this->mSRR<<" | "<<this->mIDE<<" | "<<this->mIdentifierExt.to_ulong()<<" | "
            <<this->mRTR<<" | "<<this->mR1<<" | "<<this->mR0<<" | "<<this->mDLC.to_ulong()<<" | ";
        else
            wf<<this->mSRR<<" | "<<this->mIDE<<" | "<<this->mIdentifierExt<<" | "
            <<this->mRTR<<" | "<<this->mR1<<" | "<<this->mR0<<" | "<<this->mDLC<<" | ";
    }else
    {
        if(!user || (user&&!gd))
            wf<<this->mRTR<<this->mIDE<<this->mR0<<this->mDLC;
        else if(hex)
            wf<<this->mRTR<<" | "<<this->mIDE<<" | "<<this->mR0<<" | "<<this->mDLC.to_ulong()<<" | ";
        else
            wf<<this->mRTR<<" | "<<this->mIDE<<" | "<<this->mR0<<" | "<<this->mDLC<<" | ";
    }
    if(!this->mRTR.test(0))  //The collision resolution requires that the value of dlc in remote frame equal to that of dlc in data frame
    {
        for(int i=this->mDLC.to_ulong()-1;i>=0;--i)
        {
            if( !(user&&hex&&gd) )
            {
                std::bitset<8> temp(this->mData[i]);
                wf<<temp;
            }else 
                wf<<static_cast<unsigned short> (this->mData[i]);
        }
    }
    if(!user || (user&&!gd))
        wf<<this->mCRC<<this->mCRC_del<<this->mACK<<this->mACK_del<<this->mEOFrame;
    else if(hex)
        wf<<" | "<<this->mCRC.to_ulong()<<" | "<<this->mCRC_del<<" | "<<this->mACK
        <<" | "<<this->mACK_del<<" | "<<this->mEOFrame.to_ulong()<<" | ";
    else
        wf<<" | "<<this->mCRC<<" | "<<this->mCRC_del<<" | "<<this->mACK
        <<" | "<<this->mACK_del<<" | "<<this->mEOFrame<<" | ";
    if(user&&!gd&&hex)
    {
        unsigned short temp=0;
        size_t len=wf.str().length();
        std::stringstream hexStr;
        hexStr.unsetf(std::ios::dec);
        hexStr.setf(std::ios::hex);
        if(crc) len-=10;
        for(int i=0;i<(len%16?len/16+1:len/16);++i)
        {
            for(int j=0;j<16;j++)
            {
                if(i==0&&j==0) j=len%16?16-len%16:0;
                temp<<=1;
                temp|=wf.get()-0x30;
            }
            hexStr<<temp;
        }
        return hexStr.str();      
    }else
        return wf.str();   
}
std::bitset<15> frame::calcCRC() const
{
    const unsigned short kPolynomial = 0xc599;
    std::string str=getWholeFrameInOne();
    unsigned short reg=0;
    for(int i=0;i<str.size()-10;++i)    //exclude delimiter、ack and eof
    {
        reg <<= 1;
        if(str[i]=='1')
            reg |= 0x1UL;
        if(i>=15 && reg>>15)
            reg ^= kPolynomial;
    }
    return std::bitset<15> (reg);   //lowest 15 bits of ushort copied into bitset
}

frame::frame(std::bitset<11> identifier, std::vector<unsigned char> data)
:mIdentifier(identifier),mDLC(data.size()),mData(data)
{
    preCheck();
    this->mRTR.reset();
    this->mIDE.reset();
    this->mCRC=calcCRC();
}
frame::frame(std::bitset<29> extIdentifier, std::vector<unsigned char> data)
:mDLC(data.size()),mData(data)
{
    preCheck();
    std::stringstream str;
    str<<extIdentifier;
    this->mIdentifier=std::bitset<11>(str.str().substr(0,11));
    this->mIdentifierExt=std::bitset<18>(str.str().substr(11,18));
    this->mRTR.reset();
    this->mIDE.set();
    this->mCRC=calcCRC();
}
frame::frame(std::bitset<11> identifier,unsigned long dlc,bool RTR)      //base remote frame   
:mDLC(dlc),mIdentifier(identifier)
{
    preCheck();
    if(RTR)
    {
        this->mRTR.set();
        this->mIDE.reset();
        this->mCRC=calcCRC();
    }else if(!dlc)
    {
        this->mRTR.reset();
        this->mIDE.reset();
        this->mCRC=calcCRC();       
    }
}
frame::frame(std::bitset<29> extIdentifier, unsigned long dlc, bool RTR)    //extended remote frame
:mDLC(dlc)
{
    preCheck();
    std::stringstream str;
    str<<extIdentifier;
    this->mIdentifier=std::bitset<11>(str.str().substr(0,11));
    this->mIdentifierExt=std::bitset<18>(str.str().substr(11,18));
    if(RTR)
    {
        this->mRTR.set();
        this->mIDE.set();
        this->mCRC=calcCRC();
    }else if(!dlc)
    {
        this->mRTR.reset();
        this->mIDE.set();
        this->mCRC=calcCRC();       
    }
}

frame::frame(std::string rawFrame)
{
    postCheck(rawFrame);
    int len=rawFrame.length();
    this->mIdentifier=std::bitset<11> (rawFrame.substr(1,11));
    this->mIDE[0]=asc2num(rawFrame[13]);   //bitset 代理类只认识数字1和0
    if(this->mIDE.test(0))  //EXTENDED FRAME
    {
        this->mSRR[0]=asc2num(rawFrame[12]);
        this->mIdentifierExt=std::bitset<18> (rawFrame.substr(14,18));
        this->mRTR[0]=asc2num(rawFrame[32]);
        this->mR1[0]=asc2num(rawFrame[33]);
        this->mR0[0]=asc2num(rawFrame[34]);
        this->mDLC=std::bitset<4> (rawFrame.substr(35,4));
        if(this->mDLC.to_ulong()>8)
            this->mDLC=std::bitset<4> (8);
        if(!this->mRTR.test(0))
        {
            this->mData.resize(this->mDLC.to_ulong());  //initial vector of data on size
            for(int i=this->mDLC.to_ulong()-1,j=0;i>=0;--i,++j)
            {
                                                                                            //std::bitset<8> temp(rawFrame.substr(39+j*8,8));
                this->mData[i]= std::stoul(rawFrame.substr(39+j*8,8),nullptr,2);            //temp.to_ulong();
            }
        }
        this->mCRC=std::bitset<15> (rawFrame.substr(len-25,15));
        if(this->calcCRC().any())
            throw std::invalid_argument("CRC validation failed");
        this->mACK[0]=kDominantBit;
    }else   //BASE FRAME
    {
        this->mRTR[0]=asc2num(rawFrame[12]);
        this->mR0[0]=asc2num(rawFrame[14]);
        this->mDLC=std::bitset<4> (rawFrame.substr(15,4));
        if(this->mDLC.to_ulong()>8)
            this->mDLC=std::bitset<4> (8);
        if(!this->mRTR.test(0))
        {
            this->mData.resize(this->mDLC.to_ulong());  //initial vector of data on size
            for(int i=this->mDLC.to_ulong()-1,j=0;i>=0;--i,++j)
            {
                                                                                    //std::bitset<8> temp(rawFrame.substr(19+j*8,8));
                this->mData[i]=std::stoul(rawFrame.substr(19+j*8,8),nullptr,2);     //temp.to_ulong();
            }
        }
        this->mCRC=std::bitset<15> (rawFrame.substr(len-25,15));
        if(this->calcCRC().any())
            throw std::invalid_argument("CRC validation failed");
        this->mACK[0]=kDominantBit;
    }
}
frame::frame(frame&& src) noexcept
{
    *this=std::move(src);
}
frame& frame::operator=(frame&& rhs) noexcept
{
    this->mIdentifier=rhs.mIdentifier;
    this->mRTR=rhs.mRTR;
    this->mIDE=rhs.mIDE;
    this->mSRR=rhs.mSRR;
    this->mIdentifierExt=rhs.mIdentifierExt;
    this->mR0=rhs.mR0;
    this->mR1=rhs.mR1;
    this->mDLC=rhs.mDLC;
    this->mData=std::move(rhs.mData);
    this->mCRC=rhs.mCRC;
    this->mACK=rhs.mACK;
    return *this;
}

bool frame::isRTR(int *const reqSize) const
{
    if(reqSize==nullptr)
        return this->mRTR.test(0);
    else
    {
        *reqSize=this->mDLC.to_ulong();
        return this->mRTR.test(0);
    }
}
frame& frame::fillData(const std::vector<unsigned char> &data)
{
    preCheck(data.size());
    if(this->mRTR.test(0))  //transform remote request frame into data frame.
    {
        if(this->mDLC.to_ulong()!=data.size())
            throw std::invalid_argument("The length of data can not match that required by RTR frame");
        this->mRTR.reset();
        this->mData=data;
    }else   //reset data field with specified data.
    {
        this->mData=data;
        this->mDLC=std::bitset<4> (data.size());
    }
    //re-calculate CRC
    this->mCRC.reset();     //must reset outdated CRC code to prevent data pollution. due to the data in CRC field will be regard as CRC input.
    this->mCRC=this->calcCRC();
    return *this;
}
bool frame::isIDE() const
{
    return this->mIDE.test(0);
}
bool frame::isACK() const
{
    return this->mACK.none();   //ACK bit is dominant which is zero while msg confirmed to recv.
}
bool frame::equal_id(const frame& rhs) const
{
    if(this==&rhs)
        return true;
    if(this->mIDE==rhs.mIDE)
    {
        if(this->mIdentifier==rhs.mIdentifier)
        {
            if(this->mIDE.test(0))
                return (this->mIdentifierExt==rhs.mIdentifierExt);
            else
                return true;
        }else
            return false;
    }else
        return false;
}
bool operator==(const frame& lhs,const frame& rhs)
{
    if(&lhs==&rhs)
        return true;
    if(!lhs.equal_id(rhs))
        return false;
    return lhs.mCRC==rhs.mCRC;
}
bool operator!=(const frame& lhs,const frame& rhs)
{
    return !(lhs==rhs);
}
std::ostream& operator<<(std::ostream& out,const frame& rhs)
{
    out<<rhs.getWholeFrameInOne(true);
    return out;
}

}

